/* eslint-disable @typescript-eslint/no-explicit-any */
import { cls } from '@knno/jsx';
import { Check } from '../../input/input';
import { cellStyle } from '../cell.css';
import { inlineStyle } from '../inline.css';
import { InputBase, type InputBaseProps } from '../inputBase';
import { checkInputStyle } from './check.css';

export type CheckOption =
	| {
			value: any;
			text: string;
	  }
	| string
	| number
	| boolean;

export type CheckInputProps<T extends CheckInputBase<any, any, any>> = InputBaseProps<
	T,
	unknown[]
> & {
	options: CheckOption[];
	minCount?: number;
	maxCount?: number;
	radio?: boolean;
};

abstract class CheckInputBase<
	P extends CheckInputProps<C>,
	C extends CheckInputBase<P, C, E>,
	E extends HTMLElement,
> extends InputBase<P, C, E, unknown[]> {
	constructor(props: P) {
		super(props);
		this.setOptions(this.options);
		this.setMinCount(this.minCount);
		this.setMaxCount(this.maxCount);
		this.setRadio(this.radio);
		this.el.classList.add(checkInputStyle);
		this.el.addEventListener('focus', () => {
			if (!this.readOnly) {
				this.el.querySelector('input')?.focus();
			}
		});
	}
	protected override renderContent(): void {
		const checkboxes: HTMLInputElement[] = [];
		const updateValue = () => {
			const values: unknown[] = [];
			checkboxes.forEach((c, idx) => {
				if (!c.checked) return;
				const option = this.options[idx];
				if (
					typeof option === 'string' ||
					typeof option === 'number' ||
					typeof option === 'boolean'
				) {
					values.push(option);
				} else {
					values.push(option.value);
				}
			});
			this.updateValue(values);
		};
		this.el.replaceChildren(
			...this.props.options.map((o, idx) => {
				if (typeof o === 'string' || typeof o === 'number' || typeof o === 'boolean') {
					o = { value: o, text: o + '' };
				}
				const checked = this.value?.includes(o.value);
				return (
					<label>
						<Check
							name={this.name ?? 'options'}
							checked={checked}
							disabled={this.readOnly}
							ref={(c) => {
								c.onclick = (evt) => {
									if (this.readOnly) {
										evt.preventDefault();
										return;
									}
									if (this.radio) {
										checkboxes.forEach((o) => {
											if (o !== c) {
												o.checked = false;
											}
										});
									} else {
										const checkedCount = checkboxes.filter((o) => o.checked).length;
										if (this.maxCount !== undefined && checkedCount > this.maxCount) {
											evt.preventDefault();
										}
									}
									updateValue();
								};
								checkboxes.push(c);
							}}
							class="print"
							value={o.value + ''}
							onKeyDown={function (evt) {
								if (evt.key === 'Enter') {
									this.click();
								} else if (evt.key === 'ArrowLeft') {
									if (idx > 0) {
										checkboxes[idx - 1].focus();
										evt.stopPropagation();
									}
								} else if (evt.key === 'ArrowRight') {
									if (idx < checkboxes.length - 1) {
										checkboxes[idx + 1].focus();
										evt.stopPropagation();
									}
								}
							}}
						/>
						{o.text}
					</label>
				);
			}),
		);
	}
	protected override changeReadOnly(): void {
		if (this.readOnly) {
			this.el.tabIndex = 0;
			this.el.querySelectorAll('input').forEach((i) => (i.disabled = true));
		} else {
			this.el.tabIndex = -1;
			this.el.querySelectorAll('input').forEach((i) => (i.disabled = false));
		}
	}

	override setValue(value: any | undefined): this {
		if (!Array.isArray(value)) {
			value = [value];
		}
		this.props.value = value;
		this.renderContent();
		return this;
	}

	get options(): CheckOption[] {
		return this.props.options;
	}
	setOptions(options: CheckOption[]): this {
		this.props.options = options;
		this.renderContent();
		return this;
	}
	get minCount(): number | undefined {
		return this.props.minCount;
	}
	setMinCount(value: number | undefined): this {
		this.props.minCount = value;
		return this;
	}
	get maxCount(): number | undefined {
		return this.props.maxCount;
	}
	setMaxCount(value: number | undefined): this {
		this.props.maxCount = value;
		this.renderContent();
		return this;
	}
	get radio(): boolean {
		return this.props.radio ?? false;
	}
	setRadio(value: boolean): this {
		this.props.radio = value;
		this.renderContent();
		return this;
	}
}

export type CheckCellProps = CheckInputProps<CheckCell> & {
	colSpan?: number;
	rowSpan?: number;
};

export class CheckCell extends CheckInputBase<CheckCellProps, CheckCell, HTMLTableCellElement> {
	protected render(): Node {
		return (
			<td
				class={cls(this.props.class, cellStyle)}
				style={this.props.style}
				colSpan={this.colSpan}
				rowSpan={this.rowSpan}
			/>
		);
	}
	get colSpan(): number {
		return this.props.colSpan ?? 1;
	}

	setColSpan(value: number): this {
		this.props.colSpan = value;
		this.el.colSpan = value;
		return this;
	}

	get rowSpan(): number {
		return this.props.rowSpan ?? 1;
	}

	setRowSpan(value: number): this {
		this.props.rowSpan = value;
		this.el.rowSpan = value;
		return this;
	}
}

export class CheckInline extends CheckInputBase<
	CheckInputProps<CheckInline>,
	CheckInline,
	HTMLSpanElement
> {
	protected render(): Node {
		return <span class={cls(this.props.class, inlineStyle)} style={this.props.style} />;
	}
}
